﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

namespace ValidationFramework.Reflection
{
	public class CachedPropertyInfo : CachedInfoBase
	{

#if(WindowsCE || SILVERLIGHT)
		private readonly MethodInfo getMethodInfo;
#else
		private readonly FastInvokeHandler invoker;
#endif

		/// <summary>
        /// Initialize a new instance of <see cref="PropertyDescriptor"/>.
        /// </summary>
        /// <param name="propertyInfo">The <see cref="PropertyInfo"/> to wrap.</param>
        /// <param name="typeDescriptor">The <see cref="Reflection.TypeDescriptor"/> this <see cref="PropertyDescriptor"/> belongs to.</param>
        /// <param name="getMethodInfo">The get <see cref="MethodInfo"/> for the <see cref="PropertyInfo"/> being wrapped.</param>
        /// <exception cref="NullReferenceException"><paramref name="propertyInfo"/> is null.</exception>
		internal CachedPropertyInfo(PropertyInfo propertyInfo)
			: base(propertyInfo.PropertyType.TypeHandle, propertyInfo.Name)
        {
			Guard.ArgumentNotNull(propertyInfo, "propertyInfo");

			// check if generic type
            if (propertyInfo.PropertyType.IsGenericParameter)
            {
                throw new ArgumentException("propertyInfo cannot be a generic type.", "propertyInfo");
            }

			// check for getMethodInfo
			var getMethodInfo = propertyInfo.GetGetMethod(true);
			if (getMethodInfo == null)
			{
				throw new ArgumentException("Property must have a get. Private gets are allowed.", "name");
			}
			ReflectedTypeHandle = propertyInfo.ReflectedType.TypeHandle;

            IsStatic = getMethodInfo.IsStatic;
            IsVirtual = getMethodInfo.IsVirtual;
#if(WindowsCE || SILVERLIGHT)
            this.getMethodInfo = getMethodInfo;
#else
            invoker = MethodInvokerCreator.GetMethodInvoker(getMethodInfo);
#endif

        }

		/// <summary>
		/// Gets a value indicating whether the property is virtual.
		/// </summary>
		public bool IsVirtual { get; private set; }

		/// <summary>
		/// Gets a value indicating whether the <see cref="PropertyDescriptor"/> is static. 
		/// </summary>
		public bool IsStatic
		{
			get;
			private set;
		}

		public RuntimeTypeHandle ReflectedTypeHandle
		{
			get;
			set; 
		}

		/// <summary>
		/// Get the value for this <see cref="InfoDescriptor"/>.
		/// </summary>
		/// <param name="instance">The object on which to extract the field value. If a <see cref="InfoDescriptor"/> is static, this argument is ignored.</param>
		/// <returns>The value for the <see cref="InfoDescriptor"/>.</returns>
		public override object GetValue(object instance)
		{
#if(WindowsCE || SILVERLIGHT)
			return getMethodInfo.Invoke(instance, null);
#else
			return invoker(instance);
#endif
		}
	}
}
